﻿using System.Diagnostics;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using QuickGraph;
using System.Linq;

namespace ttg_internal.Utilities.PipelineManager.Diagram
{
    public class Connection : Control, IEdge<DesignerItem>, ISelectable
    {
        public Connection(DesignerItem source, DesignerItem target)
        {
            Source = source;
            Target = target;
            Unloaded += OnUnloaded;
        }

        // Dependency properties
        public static readonly DependencyProperty IsSelectedProperty = DependencyProperty.Register("IsSelected", typeof(bool), typeof(Connection), new PropertyMetadata(false, OnIsSelectedChanged));
        public static readonly DependencyProperty StrokeDashArrayProperty = DependencyProperty.Register("StrokeDashArray", typeof(DoubleCollection), typeof(Connection));
        public static readonly DependencyProperty SourceProperty = DependencyProperty.Register("Source", typeof(DesignerItem), typeof(Connection), new PropertyMetadata(OnSourceChanged));
        public static readonly DependencyProperty SourceArrowProperty = DependencyProperty.Register("SourceArrow", typeof(ArrowSymbol), typeof(Connection), new PropertyMetadata(ArrowSymbol.None));
        public static readonly DependencyProperty TargetProperty = DependencyProperty.Register("Target", typeof(DesignerItem), typeof(Connection));
        public static readonly DependencyProperty TargetArrowProperty = DependencyProperty.Register("TargetArrow", typeof(ArrowSymbol), typeof(Connection), new PropertyMetadata(ArrowSymbol.Arrow));

        // Private fields
        private Adorner _connectionAdorner;

        #region Properties

        public DesignerItem Source
        {
            get { return (DesignerItem)GetValue(SourceProperty); }
            set { SetValue(SourceProperty, value); }
        }

        public DesignerItem Target
        {
            get { return (DesignerItem)GetValue(TargetProperty); }
            set { SetValue(TargetProperty, value); }
        }

        public ArrowSymbol SourceArrow
        {
            get { return (ArrowSymbol)GetValue(SourceArrowProperty); }
            set { SetValue(SourceArrowProperty, value); }
        }

        public ArrowSymbol TargetArrow
        {
            get { return (ArrowSymbol)GetValue(TargetArrowProperty); }
            set { SetValue(TargetArrowProperty, value); }
        }

        public bool IsSelected
        {
            get { return (bool)GetValue(IsSelectedProperty); }
            set { SetValue(IsSelectedProperty, value); }
        }

        /// <summary>
        /// Pattern of dashes and gaps that is used to outline the connection path
        /// </summary>
        public DoubleCollection StrokeDashArray
        {
            get { return (DoubleCollection)GetValue(StrokeDashArrayProperty); }
            set { SetValue(StrokeDashArrayProperty, value); }
        }

        #endregion

        protected override void OnMouseDown(MouseButtonEventArgs e)
        {
            base.OnMouseDown(e);

            if ((Keyboard.Modifiers & (ModifierKeys.Shift | ModifierKeys.Control)) != ModifierKeys.None)
            {
                IsSelected = !IsSelected;
            }
            else if (!IsSelected)
            {
                Debug.Assert(Parent is DesignerCanvas);

                foreach (ISelectable item in ((DesignerCanvas)Parent).SelectedItems)
                {
                    item.IsSelected = false;
                }

                Debug.Assert(!((DesignerCanvas)Parent).SelectedItems.Any());
                IsSelected = true;
            }

            e.Handled = false;
        }

        private void ShowAdorner()
        {
            // the ConnectionAdorner is created once for each Connection
            if (_connectionAdorner == null)
            {
                Debug.Assert(Parent is DesignerCanvas);
                var designer = (DesignerCanvas)Parent;

                AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(designer);

                _connectionAdorner = new ConnectionAdorner(designer, this);
                adornerLayer.Add(_connectionAdorner);
            }

            _connectionAdorner.Visibility = Visibility.Visible;
        }

        private void HideAdorner()
        {
            if (_connectionAdorner != null)
            {
                _connectionAdorner.Visibility = Visibility.Collapsed;
            }
        }

        private void OnUnloaded(object sender, RoutedEventArgs e)
        {
            // do some housekeeping when Connection is unloaded

            // remove event handler
            //source.PropertyChanged -= new PropertyChangedEventHandler(OnConnectorPositionChanged);
            //sink.PropertyChanged -= new PropertyChangedEventHandler(OnConnectorPositionChanged);

            // remove adorner
            if (_connectionAdorner != null)
            {
                Debug.Assert(Parent is DesignerCanvas);
                var designer = (DesignerCanvas)Parent;

                AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(designer);
                adornerLayer.Remove(_connectionAdorner);
                _connectionAdorner = null;
            }
        }

        private static void OnIsSelectedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            Debug.Assert(d is Connection);
            var connection = (Connection)d;

            Debug.Assert(connection.Parent is DesignerCanvas);
            var canvas = (DesignerCanvas)connection.Parent;

            if ((bool)e.NewValue)
            {
                canvas.SelectedItems.Add(connection);
                connection.ShowAdorner();
            }
            else
            {
                canvas.SelectedItems.Remove(connection);
                connection.HideAdorner();
            }
        }

        private static void OnSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            Debug.Assert(d is Connection);

            if (e.OldValue != null)
            {
                Debug.Assert(e.OldValue is DesignerItem);
                ((DesignerItem)e.OldValue).Connections.Remove((Connection)d);
            }

            if (e.NewValue != null)
            {
                Debug.Assert(e.NewValue is DesignerItem);
                ((DesignerItem)e.NewValue).Connections.Add((Connection)d);
            }
        }
    }

    public enum ArrowSymbol
    {
        None,
        Arrow,
        Diamond
    }
}
